﻿package com.tools
{
	import flash.events.Event;
	
	import flash.display.*;
	
	import com.conf.*;
	
	import com.application.ApplicationDefault;
	
	import com.datasource.*;
	import com.datasource.interfaces.*;
	
	import com.widgets.*;
	import com.components.*;
	
	import com.tools.notifications.SkinEvent;
	
	import com.utils.UICreatorUtils;
	import com.utils.ArrayUtils;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	
	/////////////////////////////////////////////////////////////////////////////////////////////////////////
	// SkinGenerator ::
	// - gestionnaire de l'affichage de l'intégralité de la skin
	/////////////////////////////////////////////////////////////////////////////////////////////////////////
	
	public class SkinManager extends Sprite
	{
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		// MEMBRES DE CLASSE
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		
		private var initializingContainer		: Sprite; // LE CONTENEUR D'ATTENTE PENDANT LE CHARGEMENT
		private var globalContainer				: Sprite; // LE CONTENEUR GLOBAL DE LA SCENE
		private var widgetContainer				: Sprite; // LE CONTENEUR GENERIQUE DE CHAQUE WIDGET
		private var componentContainer			: DisplayObject; // LE CONTENEUR GENERIQUE DE CHAQUE COMPOSANT
		private var elementContainer			: MovieClip; // LE CONTENEUR GENERIQUE DE CHAQUE ELEMENT
		private var loaderContainer				: MovieClip;
		
		private static var _skinManager			: SkinManager;
		
		private var view						: Object; // une référence à la view ( PlayerView )
		
		// les infos passées en parametre sont stockées dans ce tableau
		private var skinVOArr					: Array;
		// variables utilisées pour l'incrémentation
		private var i							: uint;
		private var j							: uint;
		private var k							: uint;
		// variables tampons pour stocker les infos
		private var n							: String;
		private var t							: String;
		private var p							: Number;
		private var pp							: int;
		
		private var initialized					: Boolean = true;
		private var loaded						: Boolean = false;
		private var graphicsLoading				: uint = 0;
		
		private var _playerWidth				: Number;
		private var _playerHeight				: Number;
		
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		// METHODES DE GENERATION DE L'AFFICHAGE DE LA SKIN
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		// METHODE STATIQUE : vérification à l'ajout à la liste d'affichage
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		
		// implémentation du design pattern Singleton
		static public function getInstance( ) : SkinManager
		{
			if ( ! _skinManager )
				_skinManager = new SkinManager( );
			return _skinManager;
		}
		
		// ajoute l'objet à la liste d'affichage auprès d'un conteneur (avec vérification)
		static private function addToDisplayList( element : *, container : * ) : void
		{
			if	( 
					( ! ( element is DisplayObject ) )
					||
					( ! ( container is DisplayObjectContainer ) )
				)
				throw new Error ( "Vous tentez d'ajouter des objets incompatibles à la liste d'affichage" );
			else
				( ( container ) as DisplayObjectContainer ).addChild( element ); 
		}
		
		// enlève l'objet de la liste d'affichage auprès d'un conteneur (avec vérification) + supprime la variable
		static private function clearFromDisplayList( element : *, container : * ) : void
		{
			if	( 
					( ! ( element is DisplayObject ) )
					||
					( ! ( container is DisplayObjectContainer ) )
				)
				throw new Error ( "Vous tentez de retirer des objets incompatibles à la liste d'affichage" );
			else
			{
				( ( container ) as DisplayObjectContainer ).removeChild( element );
			}
		}
		
		// renvoie true si le composant spécifié dispose de plusieurs états (ex: un bouton)
		static private function hasComponentStates( comp : DisplayObject ) : Boolean
		{
			if ( 
					( comp is PlayButton )
					||
					( comp is PauseButton )
					||
					( comp is StopButton )
					||
					( comp is NextButton )
					||
					( comp is PreviousButton )
				)
				return true;
			else
				return false;
		}
		
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		// METHODES PUBLIQUES : chargement et affichage des composants la skin
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		
		// on référence la View
		// on crée le conteneur global de la skin
		public function preGenerateDisplay( pView : Object):void
		{
			this.view = pView;
			
			globalContainer = UICreatorUtils.spriteCreator(
														   	SkinClassReferencer.SPRITE_TYPE,
															PlayerSpec.ROOT_CONTAINER
															);
			initializingContainer = UICreatorUtils.spriteCreator(
														   	SkinClassReferencer.SPRITE_TYPE,
															PlayerSpec.ROOT_CONTAINER
															);
			globalContainer.visible = false;
			
			
			waitBeforeLoading( );
			
			//trace("AJOUT DU GLOBALCONTAINER PLEIN A LA SCENE");
		}
		
		// on génére l'affichage de la skin à partir des infos parsées récupérées du fichier xml
		public function generateDisplay( skinProps : Array ):void
		{
			if ( ! initialized ) initialized = true;
			if ( loaded ) loaded = false;
			if ( globalContainer.visible ) globalContainer.visible = false;
			
			// on transmets le tableau passé en paramètre à la classe
			skinVOArr = new Array();
			skinVOArr = ArrayUtils.cloneArray( skinProps );
			
			retrieveStageMeasures( skinVOArr );
			
			globalContainer.x = ( this.view.stage.stageWidth - _playerWidth ) / 2;
			globalContainer.y = ( this.view.stage.stageHeight - _playerHeight ) / 2;
			
			// on réinitialise les variables utilisées			
			n 		= new String();
			t		= new String();
			p 		= 0;
			pp		= 0;
			i 		= 0;
			j 		= 0;
			k 		= 0;
			
			generateWidgets( );
		}
		
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		// METHODES PRIVEES : mécanismes internes de l'affichage des composants la skin
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		
		// on génére chacun des widgets
		private function generateWidgets( ) : void
		{
			var nbTotalWidgets : uint = skinVOArr.length;		// Nb d'éléments à instancier (widgets, components, elements & skinloader )
						
			for ( i = 0; i < nbTotalWidgets; i++)
			{
				n = String 	( skinVOArr[i][ PlayerSpec.PROPERTY_NAME ] );
				t = String 	( skinVOArr[i][ PlayerSpec.PROPERTY_TYPE ] );
				p = Number 	( skinVOArr[i][ PlayerSpec.PROPERTY_ORDER ] );
				pp 	 = int 	( skinVOArr[i][ PlayerSpec.PROPERTY_PARENT ] );
				
				if ( skinVOArr[i] is SimpleWidgetSkinVO || skinVOArr[i] is WidgetSkinVO )
				{					
					createWidget( );
					
					generateComponents ( );
					
					addToDisplayList( widgetContainer, globalContainer );
				}
				else throw new Error( "les informations du widget sont erronées");
			}
		}
		
		// puis on génére chacun des composants d'un widget
		private function generateComponents ( ) : void
		{
			var nbTotalComponents : uint = skinVOArr[i].skinContentList.length;
			for ( j = 0 ; j < nbTotalComponents ; j++ )
			{
				var currentComponent : * = skinVOArr[i].skinContentList[j];
				if ( currentComponent is SimpleComponentSkinVO || currentComponent is ComponentSkinVO )
				{
					n	= String	( skinVOArr[i].skinContentList[j][ PlayerSpec.PROPERTY_NAME ] );
					t	= String	( skinVOArr[i].skinContentList[j][ PlayerSpec.PROPERTY_TYPE ] );
					p	= Number 	( skinVOArr[i].skinContentList[j][ PlayerSpec.PROPERTY_ORDER ] );
					pp	= int 		( skinVOArr[i].skinContentList[j][ PlayerSpec.PROPERTY_PARENT ] );
					/*
					if ( widgetContainer is ButtonBar )
					{
						*/
						trace(j + " crée bien -> " + n + " / widget : " + widgetContainer.name + " / n : " + n);
						
						componentContainer = ( ( widgetContainer ) as DisplayObjectContainer ).getChildByName( n ) as Sprite;
					/*
					}
					else
						createComponent( );*/
					
					generateElements( );
				}
				else throw new Error( "les informations du composant sont erronées" );
			}
		}
		
		// puis on génére chacun des éléments d'un composant et on y charge les graphiques
		private function generateElements ( ) : void
		{
			var nbTotalElements : uint = skinVOArr[i].skinContentList[j].skinContentList.length;
			for ( k = 0; k < nbTotalElements ; k++ )
			{
				if ( skinVOArr[i].skinContentList[j].skinContentList[k] is ElementSkinVO )
				{
					n	= String	( skinVOArr[i].skinContentList[j].skinContentList[k][ PlayerSpec.PROPERTY_NAME ] );
					t	= String	( skinVOArr[i].skinContentList[j].skinContentList[k][ PlayerSpec.PROPERTY_TYPE ] );
					p	= Number 	( skinVOArr[i].skinContentList[j].skinContentList[k][ PlayerSpec.PROPERTY_ORDER ] );
					pp	= int 		( skinVOArr[i].skinContentList[j].skinContentList[k][ PlayerSpec.PROPERTY_PARENT ] );

					if ( ! ( componentContainer is BackgroundImage ) )
						n = n + PlayerSpec.SUFFIX_STATE;
					
					elementContainer = ( ( componentContainer ) as DisplayObjectContainer ).getChildByName( n ) as MovieClip;

					loadElement( );
					
					if ( k == ( nbTotalElements -1 ) )
						addToDisplayList( componentContainer, widgetContainer );
				}
				else throw new Error( "les informations de l'élément sont erronées" );
			}
		}
		
		// crée le widget selon le nom du noeud
		private function createWidget( ) : void
		{
			switch( n )
			{
				case PlayerSpec.WDG_BUTTONBAR:
				trace("* createWidget -> ButtonBar *");
				widgetContainer = UICreatorUtils.spriteCreator(
															   	SkinClassReferencer.BUTTONBAR_TYPE,
																n
																);
				break;
				
				case PlayerSpec.WDG_BACKGROUNDCONTAINER:
				trace("* createWidget -> BackgroundContainer *");
				widgetContainer = UICreatorUtils.spriteCreator(
															   	SkinClassReferencer.BACKGROUNDCONTAINER_TYPE,
																n
																);
				break;
				
				default:
				trace("* createWidget -> widget par défaut *");
				widgetContainer = UICreatorUtils.spriteCreator(
															   	SkinClassReferencer.SPRITE_TYPE,
																n
																);
				break;
			}
		}
		
		// crée le composant approprié selon le nom du noeud
		private function createComponent( ) : void
		{
			switch( n )
			{
				case PlayerSpec.IMG_BACKGROUND:
				trace( "je passe dans IMG_BACKGROUND");
				componentContainer  = UICreatorUtils.spriteCreator( 
																	SkinClassReferencer.BACKGROUNDIMAGE_TYPE,
																	n
																	);
				break;
				
				case PlayerSpec.BTN_PLAY:
				trace( "je passe dans BTN_PLAY");
				componentContainer	= UICreatorUtils.spriteCreator( 
																	SkinClassReferencer.PLAYBUTTON_TYPE,
																	n
																	);
				break;
				
				case PlayerSpec.BTN_PAUSE:
				trace( "je passe dans BTN_PAUSE");
				componentContainer = UICreatorUtils.spriteCreator(
																	SkinClassReferencer.PAUSEBUTTON_TYPE,
																	n
																	);
				break;
				
				case PlayerSpec.BTN_STOP:
				trace( "je passe dans BTN_STOP");
				componentContainer = UICreatorUtils.spriteCreator(
																	SkinClassReferencer.STOPBUTTON_TYPE,
																	n
																	);
				break;
				
				case PlayerSpec.BTN_NEXT:
				trace( "je passe dans BTN_NEXT");
				componentContainer = UICreatorUtils.spriteCreator(
																	SkinClassReferencer.NEXTBUTTON_TYPE,
																	n
																	);
				break;
				
				case PlayerSpec.BTN_PREVIOUS:
				trace( "je passe dans BTN_PREVIOUS");
				componentContainer = UICreatorUtils.spriteCreator(
																	SkinClassReferencer.PREVIOUSBUTTON_TYPE,
																	n
																	);
				break;
				
				default: 
				trace( "je passe dans DEFAULT -> IMG_BACKGROUND");
				componentContainer  = UICreatorUtils.spriteCreator( 
																	SkinClassReferencer.BACKGROUNDIMAGE_TYPE,
																	n
																	);
				break;
			}
		}
		
		// on charge le graphique dans le composant
		private function loadElement( ) : void
		{
			if	( 
					( componentContainer is BackgroundImage )
					||
					( componentContainer is PlayButton )
					||
					( componentContainer is PauseButton )
					||
					( componentContainer is StopButton )
					||
					( componentContainer is NextButton )
					||
					( componentContainer is PreviousButton )
				)
				loadGraphic( skinVOArr[i].skinContentList[j].skinContentList[k] );
			else
				throw new Error( "le composant est de type inconnu : " + componentContainer );
		}
		
		// charge dans l'élément du composant spécifié le graphique qui convient
		protected function loadGraphic( infos : ElementSkinVO ) : void
		{			
			var skinLoader : SkinLoader = new SkinLoader( infos );
			skinLoader.name = PlayerSpec.PROPERTY_LOADER;
			skinLoader.addEventListener( SkinEvent.ELEMENT_COMPLETE, graphicLoaded );
			// on incrémente pour spécifier qu'on a ajouté un nouvel élément à charger
			graphicsLoading ++;
			addToDisplayList( skinLoader, elementContainer );
		}
		
		// méthode qui surveille l'état de chargement de la totalité des composantes de la skin
		// (chaque SkinEvent marque la fin du chargement d'un des éléments)
		private function graphicLoaded( pEvt : SkinEvent ) : void
		{
			graphicsLoading--;
			if ( graphicsLoading == 0 )
			{
				initialized = false;
				loaded = true;
				dispatchEvent( new SkinEvent( SkinEvent.SKIN_COMPLETE ) );
				onceLoadingFinished( );
			}
		}
		
		
		// implémentation ultra basique d'un panneau d'attente de chargement
		private function waitBeforeLoading( ) : void
		{
			var lng : uint = this.view.numChildren;
			if ( lng > 0 )
			{
				var container 	: DisplayObjectContainer = this.view as DisplayObjectContainer;
				var element		: DisplayObject;
				for ( var i : uint = 0 ; i < lng ; i++ )
				{
					element = container.getChildAt( i );
					trace( "element --> " + element.name );
					clearFromDisplayList( element, container );
				}
			}
			addToDisplayList( initializingContainer, this.view );
			var a :TextField = new TextField();
			a.x = this.view.stage.stageWidth / 2;
			a.y = this.view.stage.stageHeight / 2;
			a.autoSize = TextFieldAutoSize.LEFT;
            a.multiline = true;
            a.htmlText = "Chargement en cours...";
			addToDisplayList( a, initializingContainer );
		}
		
		// une fois le chargement finit, on enlève le panneau d'attente et on affiche le player
		private function onceLoadingFinished( ) : void
		{
			clearFromDisplayList ( initializingContainer, this.view );
			addToDisplayList( globalContainer, this.view );
			globalContainer.visible = true;
		}
		
		private function retrieveStageMeasures ( pArr : Array ) : void
		{
			for each( var i : * in pArr )
			{
				
				if ( i[ PlayerSpec.PROPERTY_NAME ] == PlayerSpec.CONTAINER_GLOBAL )
				{
					for each ( var j : * in i.skinContentList )
					{
						if ( j[ PlayerSpec.PROPERTY_NAME ] == PlayerSpec.IMG_SCENE )
						{
							for each ( var k : * in j.skinContentList )
							{
								if ( k[ PlayerSpec.PROPERTY_NAME ] == PlayerSpec.IMG_BACKGROUND )
								{
									_playerWidth = k[ PlayerSpec.PROPERTY_WIDTH ];
									_playerHeight= k[ PlayerSpec.PROPERTY_HEIGHT ];
								}
							}
						}
					}
				}
			}
		}
	}
}